﻿module poseidon.controller.debugcontrol.watch;

private import dwt.all;
private import poseidon.controller.editor;
private import poseidon.controller.gui;
private import poseidon.globals;
private import poseidon.i18n.itranslatable;


class CWatchPanel : CTabFolder
{
private:
	const int		TAB_HEIGHT = 24;
	int[]			lastSashWeights;
	SashForm		_parent;
	Composite		tbContainer;	// tool bar container
	StackLayout		stackLayout;

	void initGUI()
	{
		//setMinimizeVisible( true );
		
		this.setSelectionBackground( [DWTResourceManager.getColor(0, 100, 255), DWTResourceManager.getColor(113, 166, 244), null ], 
									 [60, 100], true);
		this.setTabHeight( TAB_HEIGHT );
		this.setSimple( false );
		this.setSelectionForeground( DWTResourceManager.getColor( 255,255,255 ) );
		
		varItem = new CVariableItem( this );
		bpItem = new CBreakPointItem( this );
		regItem = new CRegisterItem( this );
		stackItem = new CStackItem( this );
		dllItem = new CDllItem( this );
		this.setSelection( 0 );
		
		tbContainer = new Composite( this, DWT.NONE );
		stackLayout = new StackLayout();
		tbContainer.setLayout( stackLayout );

		Control top = varItem.getTbBar(tbContainer);
		bpItem.getTbBar( tbContainer );
		regItem.getTbBar( tbContainer );
		stackItem.getTbBar( tbContainer );
		dllItem.getTbBar( tbContainer );
		
		stackLayout.topControl = top;
		tbContainer.layout();
		setTopRight( tbContainer );
	}
	
	void onSashResize( Event e )
	{
		if( this.getMinimized() )
		{
			Point pt1 = _parent.getSize();
			int[] newWeights = [Math.max(0, pt1.y - TAB_HEIGHT), TAB_HEIGHT ];
			_parent.setWeights( newWeights );
			_parent.layout( true );
		}
	}

	void onTabFolderSelection( Event e ) 
	{
		CTabItem ti = cast(CTabItem) e.item;
		if( ti.getText() == "Registers" ) 
		{
			sActionMan.actionDebugDumpRegister( e );
		}
		else if( ti.getText() == "Stack" ) 
		{
			sActionMan.actionDebugDumpStack( e );
		}

		CWatchPanel pthis = cast(CWatchPanel) e.widget;
		CWatchItem item = cast(CWatchItem)e.item;
		pthis.stackLayout.topControl = item.getTbBar(null);
		pthis.tbContainer.layout();		
	}	

public:	
	CVariableItem	varItem;
	CBreakPointItem	bpItem;
	CRegisterItem	regItem;
	CStackItem		stackItem;
	CDllItem		dllItem;
	
	
	this( SashForm parent )
	{
		super( parent, DWT.TOP | DWT.BORDER );
		_parent = parent;
		
		initGUI();

		this.handleEvent( null, DWT.Selection, &onTabFolderSelection );
	}
}


abstract class CWatchItem : CTabItem
{
	// ToolBar container
	Composite 	tbBar = null;
	ToolItem	tiStop;
	
	public this( CTabFolder parent )
	{
		super( parent, DWT.NONE );
	}
	
	abstract public Control getTbBar(Composite container);
	
	public void bringToFront()
	{
		CWatchPanel _parent = cast(CWatchPanel) getParent();
		if(_parent.getSelection() !is this)
		{
			/** 
			 * since CTabFolder.setSelection() will not trig DWT.Selection
			 * event, must set correct top right ToolBar manually
			 */
			_parent.setSelection( this );
			_parent.stackLayout.topControl = getTbBar( null );
			_parent.tbContainer.layout();
		}
		//_parent.onRestore(null);
	}
}



class CVariableItem : CWatchItem
{
private:
	import poseidon.controller.debugcontrol.breakpoint;

	//TableTree 	tableTree;
	//Table 		table;
	Tree			tree;

	void initGUI( Composite parent )
	{
		setImage( Globals.getImage( "debug_varview") );
		tree = new Tree( parent, DWT.NONE );

		scope font = new Font( getDisplay, "Courier New", 8, DWT.BOLD );
		tree.setFont( font );
		this.setControl( tree );
	}


public:
	this( CTabFolder parent )
	{
		super( parent );
		initGUI( parent );
		updateI18N();
		//content.handleEvent(content, DWT.MouseDoubleClick, &onDBClick);
	}

	Control getTbBar( Composite container )
	{
		if( tbBar is null )
		{
			tbBar = new Composite( container, DWT.NONE );
			GridLayout gl = new GridLayout();
			tbBar.setLayout( gl );
			with( gl )
			{
				marginWidth = 0;
				marginHeight = 0;
			}
			ToolBar toolbar = new ToolBar( tbBar, DWT.FLAT | DWT.HORIZONTAL );
			toolbar.setLayoutData( new GridData( GridData.HORIZONTAL_ALIGN_END ) );

			with( new ToolItem( toolbar, DWT.CHECK ) )
			{
				setImage( Globals.getImage( "link" ) );
				setToolTipText( Globals.getTranslation( "debug.tooltip_link" ) );
				setSelection( sGUI.debuggerDMD.bLiveUpdateVar );
				handleEvent( this, DWT.Selection, delegate( Event e )
				{
					sGUI.debuggerDMD.bLiveUpdateVar = !sGUI.debuggerDMD.bLiveUpdateVar;
					setSelection( sGUI.debuggerDMD.bLiveUpdateVar );
				});				
			}
			
			with( tiStop = new ToolItem( toolbar, DWT.NONE ) )
			{
				setImage( Globals.getImage( "refresh" ) );
				setDisabledImage( Globals.getImage( "refresh_dis" ) );
				setToolTipText( Globals.getTranslation( "pop.refresh" ) );
				setEnabled( false );
				handleEvent(this, DWT.Selection, delegate(Event e)
				{
					sActionMan.actionDebugListVariable( e );
				});
			}
		}
		
		return tbBar;
	}

	void updateToolBar() 
	{
		if( sGUI.debuggerDMD is null ) return;

		if( !sGUI.debuggerDMD.isPipeCreate() )
			tiStop.setEnabled( false );
		else
			if( sGUI.debuggerDMD.isRunning) tiStop.setEnabled( true );else tiStop.setEnabled( false );
	}

	void updateVariables( bool bClean = false )
	{
		//tableTree.removeAll();
		//table.removeAll();
		if( bClean )
		{
			tree.removeAll();
			if( sGUI.debuggerDMD.variableBaseRoot !is null ) delete sGUI.debuggerDMD.variableBaseRoot;
			return;
		}

		bool bCreateNewTree = true;
		
		if( sGUI.debuggerDMD.variableBaseRoot !is null )
		{
			CVariableNode _activeNode = sGUI.debuggerDMD.variableBaseRoot;

			void _updateTableTreeItem( CVariableNode node, TreeItem tti )
			{
				TreeItem[] 	tis = tti.getItems();
				int			tItemIndex;
				
				foreach( CVariableNode v; node.getAllLeaf() )
				{
					TreeItem 	ttiChild;
					bool		bAddTreeItem;
	
					if( bCreateNewTree )
						ttiChild = new TreeItem( tti, DWT.NONE );
					else
					{
						if( tItemIndex >= tti.getItemCount )
						{
							ttiChild = new TreeItem( tti, DWT.NONE );
							bAddTreeItem = true;
						}
						else
							ttiChild = tis[tItemIndex];

						tItemIndex ++;
					}

					char[] type = v._type;

					char[] parentType = node._type;

					if( v._name.length )
					{
						if( v._name[0] == '[' )
							if( parentType.length )
								if( parentType[length-1] == ']' )
								{
									parentType = parentType[0..std.string.find( parentType, "[" )]; // get Type
									if( parentType == type ) type = node._name;
								}
					}

					

					ttiChild.setText( type ~ " " ~ v._name ~ " = " ~ v._value ~ ( v._hex.length ? " (" ~ v._hex ~ ")" :"" )  ~
										( v._ext.length ? " " ~ v._ext : "" )  );

					char[] iconName;
					if( v._type.length )
					{
						int index = std.string.find( v._type, " " );
						if( index > -1 ) iconName = v._type[0..index];
					}
					
					switch( iconName )
					{
						case "class":
						case "class*":
							ttiChild.setImage( Globals.getImage( "D_CLASS" ) );
							break;
						case "struct":
						case "struct*":
							ttiChild.setImage( Globals.getImage( "D_STRUCT" ) );
							break;
						case "union":
							ttiChild.setImage( Globals.getImage( "D_UNION" ) );
							break;
						case "enum":
							ttiChild.setImage( Globals.getImage( "D_ENUM" ) );
							break;
						default:
							ttiChild.setImage( Globals.getImage( "D_VARIABLE" ) );
							break;
							
					}
					
					if( v.getLeafCount )
					{
						_updateTableTreeItem( v, ttiChild );
						if( bCreateNewTree || bAddTreeItem ) ttiChild.setExpanded( true );
					}
					else
					{
						if( !bCreateNewTree )
						{
							foreach( TreeItem ti; ttiChild.getItems() )
								ti.dispose();
						}
					}
				}

				if( !bCreateNewTree )
				{
					for( int i = tItemIndex; i < tti.getItemCount(); ++ i )
						tis[i].dispose();
				}
			}


			if( tree.getItemCount() && _activeNode.getLeafCount() )
			{
				TreeItem[] tis;
				tis = tree.getItems();
				
				TreeItem 		firstTreeItem = tis[0];
				CVariableNode	firstVariableNode = _activeNode.getLeaf( 0 );
				
				if( firstTreeItem.getText() == ( "Scope: " ~ firstVariableNode._scope ) ) bCreateNewTree = false;
			}

			if( bCreateNewTree ) tree.removeAll();			

			// 先找根節點 ( Scope )
			int 		tItemIndex;

			
			foreach( CVariableNode v; _activeNode.getAllLeaf() )
			{
				TreeItem 	ttiScope;
				
				if( bCreateNewTree )
				{
					ttiScope = new TreeItem( tree, DWT.NONE );
				}
				else
				{
					TreeItem[] tis;
					tis = tree.getItems();
					
					ttiScope = tis[tItemIndex];
					tItemIndex ++;
				}

				ttiScope.setText( "Scope: " ~ v._scope );
				ttiScope.setImage( Globals.getImage( "D_ALIAS" ) );
				_updateTableTreeItem( v, ttiScope );
				if( bCreateNewTree ) ttiScope.setExpanded( true );
			}

			delete sGUI.debuggerDMD.variableBaseRoot;
		}
	}
	
	void updateI18N(){ this.setText( Globals.getTranslation( "debug.vars" ) ); }
}



class CBreakPointItem : CWatchItem
{
private:
	import poseidon.controller.edititem;
	import poseidon.controller.debugcontrol.breakpoint;


	Table		table;

	private void initGUI(Composite parent)
	{
		setImage( Globals.getImage( "debug_bpview") );
		table = new Table( parent, DWT.CHECK );
		scope font = new Font( getDisplay, "Courier New", 9, DWT.BOLD );
		table.setFont( font );			
		this.setControl( table );

		table.handleEvent( null, DWT.DefaultSelection, &onDefaultSelection );
	}

	void onDefaultSelection( Event e )
	{
		TableItem item = cast(TableItem) e.item;
		CBreakPoint bp = cast(CBreakPoint) item.getData();

		EditItem ei = sGUI.editor.findEditItem( bp.fullPath );
		if( ei !is null )
		{
			//auto WaitCursor wc = new WaitCursor( getShell( ));
		
			char[] path = ei.getFileName();
			if( sGUI.editor.isFileOpened( path ) ) sGUI.editor.openFile( path, null, -1, false );

			sGUI.editor.setSelectionAndNotify( ei );
			ei.scintilla.forceFocus();
			ei.scintilla.call( 2234, bp.lineNum - 1 );
			ei.setSelection( bp.lineNum - 1 );
		}
	}

public:
	this( CTabFolder parent )
	{
		super( parent );
		initGUI( parent );
		updateI18N();
		//content.handleEvent(content, DWT.MouseDoubleClick, &onDBClick);
	}
	
	Control getTbBar( Composite container )
	{
		if( tbBar is null )
		{
			tbBar = new Composite( container, DWT.NONE );
			GridLayout gl = new GridLayout();
			tbBar.setLayout( gl );
			with( gl )
			{
				marginWidth = 0;
				marginHeight = 0;
			}
			ToolBar toolbar = new ToolBar( tbBar, DWT.FLAT | DWT.HORIZONTAL );
			toolbar.setLayoutData( new GridData( GridData.HORIZONTAL_ALIGN_END ) );

			with( new ToolItem( toolbar, DWT.NONE ) ) setEnabled( false );
			tiStop = new ToolItem( toolbar, DWT.NONE );
			tiStop.setImage(Globals.getImage( "close_view") );
			tiStop.setToolTipText( Globals.getTranslation( "debug.tooltip_cleanbps" ) );
			tiStop.handleEvent(this, DWT.Selection, delegate( Event e )
			{
				CBreakPointItem pthis = cast(CBreakPointItem)e.cData;
				
				TableItem[] tis = pthis.table.getItems();
				for( int i = 0; i < tis.length; ++ i )
				{
					CBreakPoint bp = cast(CBreakPoint) tis[i].getData();
					if( bp !is null )
					{
						if( sGUI.debuggerDMD.isPipeCreate() )
						{
							sGUI.debuggerDMD.write( "dbp " ~ bp.fullPath ~ ":" ~ std.string.toString( bp.lineNum ) ~ "\n" );
						}
						
						EditItem ei = sGUI.editor.findEditItem( bp.fullPath );
						if( ei !is null ) ei.deleteAllDebugMarker();
						delete bp;
					}
				}

				pthis.table.removeAll();
			});
		}
		return tbBar;
	}
	
	void updateI18N(){ this.setText( Globals.getTranslation( "debug.bps" ) ); }

	void add( char[] fullPath, int lineNum )
	{
		TableItem item = new TableItem( table, DWT.NULL );
		item.setData( new CBreakPoint( fullPath, lineNum ) );
		item.setText( std.path.getName( std.path.getBaseName( fullPath ) ) ~ " [line: " ~ std.string.toString( lineNum ) ~ "] - " ~ fullPath );
		item.setImage( Globals.getImage( "debug_bp") );
		item.setChecked( true );
	}

	void del( char[] fullPath, int lineNum )
	{
		TableItem[] tis = table.getItems();
		for( int i = 0; i < tis.length; ++ i )
		{
			CBreakPoint bp = cast(CBreakPoint) tis[i].getData();
			if( bp.fullPath == fullPath && bp.lineNum == lineNum )
			{
				delete bp;
				table.remove( i );
				break;
			}
		}
	}

	int getCount(){ return table.getItemCount(); }

	TableItem[] getAllItems(){ return table.getItems(); }

	CBreakPoint[] getBPs()
	{
		CBreakPoint[] bps;
		
		foreach( TableItem ti; table.getItems() )
		{
			CBreakPoint bp = cast(CBreakPoint) ti.getData();
			bps ~= bp;
		}

		return bps;
	}
}


class CRegisterItem : CWatchItem
{
private:
	Table			table;
	TableItem[5] 	tbItem;
	

	void initGUI( Composite parent )
	{
		setImage( Globals.getImage( "debug_register") );

		table = new Table( parent, DWT.BORDER );
        table.setHeaderVisible( true );
		table.setLinesVisible( true );
		
		scope font = new Font( getDisplay, "Courier New", 9, DWT.BOLD );
		table.setFont( font );

		TableColumn[8] tc;
		int i;

		for( i = 0; i < 8; ++ i )
		{
			tc[i] = new TableColumn( table, DWT.CENTER );
			if( i % 2 == 0 ) 
				tc[i].setText( Globals.getTranslation( "debug.reg" ) );
			else
				tc[i].setText( "  " ~ Globals.getTranslation( "debug.value" ) ~ "  " );

			tc[i].pack();
		}

        with( tbItem[0] = new TableItem( table, DWT.NONE ) )
		{
			setText( [ "EAX", "", "EBX", "", "ECX", "", "EDX", "" ] );
			for( i = 0; i < 8; i += 2 )
				setBackground( i, new Color( display, 0x8f, 0xff, 0x8f ) );
		}
		
        with( tbItem[1] = new TableItem( table, DWT.NONE ) )
		{
			setText( [ "EDI", "", "ESI", "", "EBP", "", "ESP", "" ] );
			for( i = 0; i < 8; i += 2 )
				setBackground( i, new Color( display, 0x8f, 0xff, 0x8f ) );
		}

		with( tbItem[2] = new TableItem( table, DWT.NONE ) )
		{
			setText(  [ "EIP", "", "EFL", "", "", "", "", "" ] );
			for( i = 0; i < 8; i += 2 )
				setBackground( i, new Color( display, 0x8f, 0xff, 0x8f ) );
		}
		
		with( tbItem[3] = new TableItem( table, DWT.NONE ) )
		{
			setText( [ "CS", "", "DS", "", "ES", "", "FS", "" ] );
			for( i = 0; i < 8; i += 2 )
				setBackground( i, new Color( display, 0x8f, 0xff, 0x8f ) );
		}

		with( tbItem[4] = new TableItem( table, DWT.NONE ) )
		{
			setText( [ "GS", "", "SS", "", "", "", "", "" ] );
			for( i = 0; i < 8; i += 2 )
				setBackground( i, new Color( display, 0x8f, 0xff, 0x8f ) );
		}

		this.setControl( table );
	}

	
public:
	this( CTabFolder parent )
	{
		super( parent );
		initGUI( parent );
		updateI18N();
		//content.handleEvent(content, DWT.MouseDoubleClick, &onDBClick);
	}

	Control getTbBar( Composite container )
	{
		if( tbBar is null )
		{
			tbBar = new Composite( container, DWT.NONE );
			GridLayout gl = new GridLayout();
			tbBar.setLayout( gl );
			with( gl )
			{
				marginWidth = 0;
				marginHeight = 0;
			}
			ToolBar toolbar = new ToolBar( tbBar, DWT.FLAT | DWT.HORIZONTAL );
			toolbar.setLayoutData( new GridData( GridData.HORIZONTAL_ALIGN_END ) );

			with( new ToolItem( toolbar, DWT.NONE ) ) setEnabled( false );
			with( tiStop = new ToolItem( toolbar, DWT.NONE ) )
			{
				setImage( Globals.getImage( "refresh" ) );
				setDisabledImage( Globals.getImage( "refresh_dis" ) );
				setToolTipText( Globals.getTranslation( "pop.refresh" ) );
				setEnabled( false );
				handleEvent(this, DWT.Selection, delegate(Event e)
				{
					sActionMan.actionDebugDumpRegister( e );
				});
			}
		}
		
		return tbBar;
	}

	void updateItems( char[][] regs )
	{
		if( regs.length == 16 )
		{
			for( int i = 0; i < 2; ++ i )
				for( int j = 0; j < 4; ++j )
					tbItem[i].setText( 1 + j * 2, regs[j + i * 4] );

			tbItem[2].setText( 1, regs[8] );
			tbItem[2].setText( 3, regs[9] );

			for( int j = 0; j < 4; ++j )
				tbItem[3].setText( 1 + j * 2, regs[10 + j] );
			
			tbItem[4].setText( 1, regs[14] );
			tbItem[4].setText( 3, regs[15] );
		}
		else
		{
			for( int i = 0; i < 5; ++ i )
				for( int j = 0; j < 4; ++j )
					tbItem[i].setText( 1 + j * 2, "" );
		}
	}

	void updateToolBar() 
	{
		if( sGUI.debuggerDMD is null ) return;

		if( !sGUI.debuggerDMD.isPipeCreate() )
			tiStop.setEnabled( false );
		else
			if( sGUI.debuggerDMD.isRunning ) tiStop.setEnabled( true );else tiStop.setEnabled( false );
	}	
	
	void updateI18N(){ this.setText( Globals.getTranslation( "debug.regs" ) ); }
}

class CStackItem : CWatchItem
{
private:
	Table		table;
	

	void initGUI( Composite parent )
	{
		setImage( Globals.getImage( "debug_stack") );
		table = new Table( parent, DWT.BORDER );
        //table.setHeaderVisible( false );
		table.setLinesVisible( true );
		scope font = new Font( getDisplay, "Courier New", 9, DWT.BOLD );
		table.setFont( font );
		this.setControl( table );
	}

	
public:
	this( CTabFolder parent )
	{
		super( parent );
		initGUI( parent );
		updateI18N();
		//content.handleEvent(content, DWT.MouseDoubleClick, &onDBClick);
	}

	Control getTbBar( Composite container )
	{
		if( tbBar is null )
		{
			tbBar = new Composite( container, DWT.NONE );
			GridLayout gl = new GridLayout();
			tbBar.setLayout( gl );
			with( gl )
			{
				marginWidth = 0;
				marginHeight = 0;
			}
			ToolBar toolbar = new ToolBar( tbBar, DWT.FLAT | DWT.HORIZONTAL );
			toolbar.setLayoutData( new GridData( GridData.HORIZONTAL_ALIGN_END ) );

			with( new ToolItem( toolbar, DWT.NONE ) ) setEnabled( false );
			with( tiStop = new ToolItem( toolbar, DWT.NONE ) )
			{
				setImage(Globals.getImage( "refresh" ) );
				setDisabledImage( Globals.getImage( "refresh_dis" ) );
				setToolTipText( Globals.getTranslation( "pop.refresh" ) );
				setEnabled( false );
				handleEvent(this, DWT.Selection, delegate(Event e)
				{
					sActionMan.actionDebugDumpStack( e );
				});
			}
		}
		
		return tbBar;
	}
	
	void updateI18N(){ this.setText( Globals.getTranslation( "debug.stack" ) ); }

	void updateToolBar() 
	{
		if( sGUI.debuggerDMD is null ) return;

		if( !sGUI.debuggerDMD.isPipeCreate() )
			tiStop.setEnabled( false );
		else
			if( sGUI.debuggerDMD.isRunning ) tiStop.setEnabled( true );else tiStop.setEnabled( false );
	}		

	void add( char[] str )
	{
		TableItem item = new TableItem( table, DWT.NULL );
		item.setText( str );
	}

	void delAll()
	{
		table.removeAll();
	}	
}


class CDllItem : CWatchItem
{
private:
	Table		table;

	void initGUI( Composite parent )
	{
		setImage( Globals.getImage( "debug_dll") );
		table = new Table( parent, DWT.BORDER );
        table.setHeaderVisible( true );
		table.setLinesVisible( true );
		
		scope font = new Font( getDisplay, "Courier New", 9, DWT.BOLD );
		table.setFont( font );

		TableColumn[2] tc;
		int i;

		tc[0] = new TableColumn( table, DWT.LEFT );
		tc[0].setText( Globals.getTranslation( "debug.base" ) );
		tc[1] = new TableColumn( table, DWT.LEFT );
		tc[1].setText( Globals.getTranslation( "debug.name" ) );

		tc[0].setWidth( 150 );
		tc[1].setWidth( 200 );

		//tc[0].pack();
		//tc[1].pack();

		this.setControl( table );
	}

	
public:
	this( CTabFolder parent )
	{
		super( parent );
		initGUI( parent );
		updateI18N();
	}

	Control getTbBar( Composite container )
	{
		if( tbBar is null )
		{
			tbBar = new Composite( container, DWT.NONE );
			GridLayout gl = new GridLayout();
			tbBar.setLayout( gl );
			with( gl )
			{
				marginWidth = 0;
				marginHeight = 0;
			}
			ToolBar toolbar = new ToolBar( tbBar, DWT.FLAT | DWT.HORIZONTAL );
			toolbar.setLayoutData( new GridData( GridData.HORIZONTAL_ALIGN_END ) );

			with( new ToolItem( toolbar, DWT.NONE ) ) setEnabled( false );
			with( tiStop = new ToolItem( toolbar, DWT.NONE ) )
			{
				setImage(Globals.getImage( "refresh" ) );
				setDisabledImage( Globals.getImage( "refresh_dis" ) );
				setToolTipText( Globals.getTranslation( "pop.refresh" ) );
				setEnabled( false );
				handleEvent(this, DWT.Selection, delegate(Event e)
				{
					sActionMan.actionListDlls( e );
				});
			}
		}
		
		return tbBar;
	}
	
	void updateI18N(){ this.setText( Globals.getTranslation( "debug.dll" ) ); }

	void updateToolBar() 
	{
		if( sGUI.debuggerDMD is null ) return;

		if( !sGUI.debuggerDMD.isPipeCreate() )
			tiStop.setEnabled( false );
		else
			if( sGUI.debuggerDMD.isRunning ) tiStop.setEnabled( true );else tiStop.setEnabled( false );
	}		

	void add( char[][] baseAndName )
	{
		TableItem item = new TableItem( table, DWT.NULL );
		item.setText( baseAndName );
	}

	void delAll(){ table.removeAll(); }	
}


